[Feature] azd update command, auto-update, and channel management#6942
[Feature] azd update command, auto-update, and channel management#6942rajeshkamal5050 merged 10 commits intoAzure:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Introduces an (alpha/hidden) self-update capability for the azd CLI, including channel selection (stable/daily) and an opt-in background stage/apply auto-update flow, with supporting telemetry and CI artifact publishing.
Changes:
- Added
azd update(hidden, alpha-gated) plus update configuration (channel, auto-update, check interval). - Implemented update manager for version checks, downloads, staging/applying binaries, and Windows MSI/package-manager aware update paths.
- Updated startup/version flows: background update checks + optional staging, applied-update banner, channel suffix in
azd version, and added telemetry fields/error mapping.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| eng/pipelines/templates/stages/publish.yml | Publishes version.txt for daily builds to support daily version checks. |
| cli/azd/resources/alpha_features.yaml | Adds update alpha feature toggle entry. |
| cli/azd/pkg/update/manager.go | Core update logic (check/download/stage/apply/replace/verify). |
| cli/azd/pkg/update/manager_test.go | Unit tests for update manager behaviors. |
| cli/azd/pkg/update/config.go | Update config + cache read/write helpers (channel/auto-update/interval). |
| cli/azd/pkg/update/config_test.go | Unit tests for update config + cache compatibility. |
| cli/azd/pkg/update/errors.go | Typed update errors with telemetry result codes. |
| cli/azd/main.go | Startup auto-update apply/stage flow + updated out-of-date banner logic. |
| cli/azd/internal/tracing/fields/fields.go | Adds update-related telemetry attribute keys. |
| cli/azd/internal/cmd/errors.go | Maps UpdateError codes into telemetry errorCode. |
| cli/azd/cmd/update.go | Implements azd update command/action (alpha-gated). |
| cli/azd/cmd/version.go | Adds optional channel suffix display when update alpha enabled. |
| cli/azd/cmd/root.go | Registers the hidden update command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fbdb4c1 to
723509e
Compare
723509e to
2d57468
Compare
hemarina
left a comment
There was a problem hiding this comment.
Should we remainder users to check env if current version is dev version for example azd version 0.0.0-dev.0 (commit 0000000000000000000000000000000000000000)? When I'm in dev version, the azd update runs successfully and I would assume azd version is up to date but I'm actually in dev version. This might be confusing to users.
Installs PR Azure#6942 MSI to Program Files, then runs azd update from that location to test msiexec replacing an in-use binary.
wbreza
left a comment
There was a problem hiding this comment.
Consolidated Code Review — PR #6942 vs Design Review (#6910)
TL;DR
The implementation is well-engineered at the code level — clean separation of concerns, thoughtful platform handling, solid test coverage, and proper integration with existing telemetry and error infrastructure. However, none of the 4 critical findings from the design review (#6910) have been addressed, and several new implementation-level issues were found.
Design Feedback Resolution
| Category | Raised in #6910 | Addressed in #6942 | Unresolved |
|---|---|---|---|
| 🔴 Critical | 4 | 0 | 4 |
| 🟠 High | 8 | 1 (partial) | 7 |
| 🆕 New in implementation | — | — | 8 |
🔴 Still-Unresolved Criticals from #6910
-
Unsigned binaries accepted —
verifyStagedBinarystill allows unsigned binaries ("not signed at all" → OK). Note: danieljurek commented "Sha does not need to be checked as the download is occurring over HTTPS only" — if the team has accepted this, please document the threat model decision. -
No rollback/recovery —
ApplyStagedUpdate()overwrites the current binary with no backup. A broken update requires manual reinstall. -
No crash-loop guard — No sentinel file or
AZD_SKIP_AUTO_UPDATEescape hatch. The marker file is display-only. If the new binary panics during startup before completing initialization, users have no env var to bypass auto-update. -
No version pinning — No
--versionflag or.azd-versionfile.azd updatealways gets "latest".
🟠 Still-Unresolved Highs from #6910
- No checksum verification — Downloads trust HTTPS only
- TOCTOU between stage and apply — No integrity hash stored at staging time; staged binary sits in user-writable space
- "daily" vs "nightly" naming — Team disagreement unresolved
- MSI for all script installs — Even PowerShell-script-installed azd uses MSI (escalates privilege requirements)
- Command conflates config with action —
azd update --auto-update on --channel dailydoes both - "update" vs "upgrade" naming — hemarina: "upgrade" for consistency; kristenwomack: "update" is standard
- No concurrent update locking — No file locking on staging directory
🆕 New Implementation Issues
See inline comments for details on each.
✅ What Looks Good
- Staged update architecture follows VS Code/Chrome pattern — download in background, apply on next startup
- Multi-layered opt-in (alpha flag + auto-update setting + CI detection) is appropriately conservative
- ETXTBSY handling on Linux (remove-then-create in
copyFile) is a thoughtful detail many implementations miss - Platform-specific MSI/detached process correctly handles binary-in-use constraint on Windows
- Telemetry integration with structured error codes follows existing
MapErrorpatterns perfectly - Comprehensive test coverage — unit tests for parsing, caching, extraction, staging lifecycle, HTTP transport, and error cases
- Error types with
UpdateErrorintegrate cleanly with existing error infrastructure
Overall Assessment: Request changes
This looks like a GA-ready roadmap for the feature. |
|
@wbreza Thanks for the comments 👍 All fixed in c9db168: Pending enhancements tracked as new issues under epic #6721: |
Addressed feedback comments - #6942 (comment)
|
/azp run azure-dev - cli |
|
Azure Pipelines failed to run 1 pipeline(s). |
Implements azd update (epic Azure#6721) as a hidden command behind the alpha.update feature toggle (default: off). Features: - Manual update via 'azd update' with install-method-aware strategy (brew/winget/choco delegation, direct binary download for scripts) - Channel switching (stable/daily) with downgrade confirmation - Auto-update: background staging + apply on next startup with re-exec - SHA256 checksum and code signing verification (macOS/Windows) - Elevation-aware auto-update (graceful fallback to warning for system installs, silent apply for user-writable locations) - Daily version tracking via single-line version.txt with build number parsed from the daily.N suffix - CI detection to skip auto-update in pipelines - 13 telemetry error codes integrated into MapError pipeline - azd version shows channel suffix when feature is enabled - Zero behavior change when alpha.update is off Pipeline changes: - Daily publish writes version.txt with full version string - SHA256 checksum generation for binary downloads Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On Linux, os.Create fails with ETXTBSY when overwriting a running executable. Fix copyFile to unlink the old file first (the running process retains its fd via the inode), then create a new file at the same path. If the unlink fails with permission denied (e.g. root-owned directory), return the permission error so ApplyStagedUpdate can trigger the elevation/keep-staged path. Also preserve source file permissions via os.Chmod after remove-then- create, since the new file would otherwise get default 0666 permissions instead of the original executable 0755. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dev builds should not self-update — developers build from source. Returns a clear error message instead of silently replacing the dev binary with a release build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
msiexec cannot replace a locked executable. Previously azd ran msiexec as a child process and waited. msiexec would kill the parent azd before completing, leaving the binary unreplaced. Now msiexec is spawned detached with CREATE_NEW_PROCESS_GROUP and DETACHED_PROCESS on Windows so azd can exit normally and release the binary lock. msiexec then completes the replacement independently. Always writes verbose MSI log to ~/.azd/logs/msi-update.log for diagnosability. Debug mode shows the log path and msiexec command line. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The auto-enable logic re-enables the alpha feature on every run, so telling users to unset it is confusing since it has no effect. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ontext timeout
- N1+C1: Code signature verification hard-fails with CodeSignatureInvalid
instead of silently skipping on error
- N2+N6: Inject *http.Client into Manager with 30s timeout, replacing
http.DefaultClient usage (resolves gosec G704 SSRF warnings)
- N3: Background goroutine uses context.WithTimeout(60s) instead of
context.Background() to prevent hung goroutines
- N4: MSI download URL uses runtime.GOARCH instead of hardcoded amd64
- N5: Alpha auto-enable banner clarifies what gets enabled
- N7: Archive extraction uses isAzdBinary() to match exact name or
platform-specific name (azd-{os}-{arch}), not broad prefix
- N8: Tests inject HTTP client via constructor instead of swapping
http.DefaultTransport globally
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
c9db168 to
e4d3be2
Compare
The new pwsh and AzurePowerShell steps were indented 2 spaces too deep, making them appear nested under the previous template's parameters block instead of as sibling steps. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
wbreza
left a comment
There was a problem hiding this comment.
Re-review Summary (PR #6942)
Re-evaluating my previous Critical findings with additional design context from the team:
Previous Criticals — Revised
| Issue | Previous | Revised | Rationale |
|---|---|---|---|
| Unsigned binaries | Critical | Resolved | Code signature now hard-fails for invalid signatures. Unsigned dev builds being allowed is intentional |
| No rollback | Critical | Medium | For a CLI tool with signature verification, corrupt update risk is low. Users can reinstall. Nice-to-have, not a blocker |
| No crash-loop guard | Critical | Medium | ApplyStagedUpdate consumes staged binary, so auto-update won't loop. Alpha feature with CI exclusion |
| No version pinning | Critical | By Design | azd update is forward-only, not a package manager |
What Improved Since Last Review
- HTTP client injection for testability
- Code signature hard-fail for invalid signatures
- Context timeout added
- Dev build blocking (0.0.0-dev.0)
- Linux ETXTBSY handling
Remaining Suggestion
Consider documenting the recovery path (reinstall via curl or MSI) in the azd update output or help text, so users know what to do if an update goes wrong.
Assessment: Progress made on previous feedback. The design decisions around no version pinning and forward-only updates make sense for the tool's use case.
wbreza
left a comment
There was a problem hiding this comment.
Code Review - Prior Feedback Verification
All prior review items addressed
I verified all 23 prior review comment threads across Copilot (9), wbreza (8), hemarina/vhvb1989 (2), danieljurek (3), and weikanglim (1).
| Status | Count |
|---|---|
| Fully Resolved | 17 |
| Partially Addressed (justified) | 3 |
| Unresolved Discussion | 1 |
| New Findings (Medium) | 2 |
Key resolutions since last review:
- Code signature verification now returns hard errors instead of swallowing failures
- HTTP client has 30s timeout (no more unbounded DefaultClient)
- Background goroutine uses context.WithTimeout(60s) + cancel
- MSI URL uses runtime.GOARCH (ARM64-safe)
- Archive extraction uses exact binary name matching
- Tests use injected HTTP clients instead of modifying global DefaultTransport
- StageUpdate correctly skips on Windows
- Alpha auto-enable shows user-facing heads-up message
Remaining minor items (non-blocking):
- replaceBinary/ApplyStagedUpdate copy is not atomic (temp+rename) - mitigated by staged binary verification on next startup
- verifyStagedBinary vs verifyCodesignMac have different unsigned-binary policies - intentional for dev builds in auto-apply path
- IoC bypass in main.go is justified (background check runs before container init)
What Looks Good:
- Comprehensive table-driven tests (650+ lines) with httptest.NewServer and injected clients
- Clean separation: config, errors, platform-specific MSI, core logic
- Proper context propagation throughout
- Well-structured telemetry with clear result codes
- Install-method-aware update dispatch
Overall: Approving - all prior feedback addressed or tracked in follow-up issues (#6982, #6985, #6986, #6987).
Feature
Adds a hidden and feature-toggled
azd updatecommand for self-updating azd — backgroundupdate checks, staged binary downloads with verification, and channel switching (stable ↔ daily).
Design Doc: #6910
Epic: #6721
Closes #6675
Closes #6722
Closes #6723
Closes #6724
Closes #6726
Follow-up enhancements (tracked under epic #6721)
Changes
azd updatecommand (cmd/update.go) — Hidden, auto-enables alpha feature on first run. Downloads, verifies, and applies updates.--channelflag for switching between stable/daily with confirmation prompt.pkg/update/manager.go) — Version check via GitHub releases API, download, staged binary verification (codesign on macOS, min size check), and safe file replacement. Injected*http.Clientfor testability.pkg/update/config.go) —updates.channelandupdates.autoUpdatesettings, gated behindupdatealpha feature flag.main.go) — Background check on every invocation with 60s context timeout, stages binaries, auto-applies or shows elevation warning on next run.brew upgrade(macOS),winget upgrade/choco upgrade(Windows). Script installs use direct binary replacement.msiexec /i. msiexec is spawned detached (CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS) so azd can exit cleanly and release the binary lock before msiexec replaces it.cmd/version.go) — Shows(daily)or(stable)channel suffix, channel-aware "out of date" warnings.internal/tracing/fields/fields.go) — Update outcome, channel, method, error codes.eng/pipelines/) — Publishesversion.txtalongside daily builds.pkg/update/*_test.go) — Covers config, update checking, download, verification, staging, and apply flows.E2E Testing
30 test cases across macOS, Linux, and Windows — all passing.
--no-promptused in Linux/Windows CI--no-promptused in Linux/Windows CImsiexec /ichoco upgradesuccessfulPlatform Notes
.zip, verifies Apple codesign.golang:1.25(linux/amd64). Downloads.tar.gz. ETXTBSY fix (remove-then-create incopyFile) enables C1 auto-apply.windows-latest(Server 2025), full suite run #22610574260 — 38/38 steps pass. Uses MSI installer. W6 validates detached msiexec — binary replaced in ~1.7s while azd exits cleanly (exit code 0).Known Limitations
version.txtdoesn't match actual binary version. Pipeline fix needed (not an azd code issue).StageUpdate()skips on Windows by design. Windows uses MSI viamsiexec, not binary replacement.winget upgraderequires terms acceptance, which can't be automated in CI.Full E2E test details with logs: https://gist.github.com/rajeshkamal5050/d509c03e30a7249a6a4a931ab761a286